package com.project.frame.common.model;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.project.frame.common.annotation.SqlQueryDateFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.lang.reflect.*;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * desc
 *
 * @author lw007
 * @date 2021/12/24
 */
@Data
public class BaseModel<T extends BaseModel<?>> extends Model {
    public static final int maxLimit = 10000;
    @ApiModelProperty(value = "每页记录条数",example ="10")
    @TableField(exist = false)
    private int limit = 10;
    @ApiModelProperty(value = "页码",example ="1")
    @TableField(exist = false)
    private int page = 1;
    @TableField(exist = false)
    protected Class<T> tclass;
    //key-列名, value-排序 desc / asc , 默认为asc
    @TableField(exist = false)
    private Map<String,String> orderMap;
    //其他特殊条件，比如 status in (1,2) , {status:[1,2]}
    // status is null, {status:null} or {status:''}
    @TableField(exist = false)
    private Map<String,Object> searchMap;

    public BaseModel(){
        tclass = (Class<T>) getModelClass(tclass, 0);
    }
    @SuppressWarnings("unchecked")
    public Class<?> getModelClass(Class modelClass,int index){
        Type genType = this.getClass().getGenericSuperclass();// 得到泛型父类
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();//一个泛型类可能有多个泛型形参，比如ClassName<T,K> 这里有两个泛型形参T和K，Class Name<T> 这里只有1个泛型形参T
        if(params.length-1<index){
            modelClass=null;
        }else{
            modelClass=(Class) params[index];
        }

        return modelClass;

    }

    public QueryWrapper<T> toWrapper(){
        QueryWrapper<T> ew = new QueryWrapper();
        Object obj = this;
        Field[] fields = tclass.getDeclaredFields();
        List<Class> dateClassList = new ArrayList<>();
        dateClassList.add(Date.class);
        dateClassList.add(java.sql.Date.class);
        dateClassList.add(Timestamp.class);
        dateClassList.add(LocalDate.class);
        dateClassList.add(LocalDateTime.class);

        String sqlDateFormat = "%Y-%m-%d";

        for(Field f:fields){
            if(Modifier.toString(f.getModifiers()).contains("final")){
                continue;
            }
            String columnName = null;
            Object val = null;
            Object anno = f.getAnnotation(TableField.class);
            if(anno==null){
                anno = f.getAnnotation(TableId.class);
                if(anno!=null){
                    columnName = f.getAnnotation(TableId.class).value();
                    val = getFieldValueByName(f.getName(),obj);
                    if(!Objects.isNull(val)){
                        ew.eq(columnName,val);
                    }
                    continue;
                }
            }

            if(anno==null){
                continue;
            }
            columnName = f.getAnnotation(TableField.class).value();
            val = getFieldValueByName(f.getName(),obj);
            String propertyName = f.getName();

            SqlQueryDateFormat sqdfAnno = f.getAnnotation(SqlQueryDateFormat.class);
            if(sqdfAnno!=null){
                sqlDateFormat = sqdfAnno.format();
            }
            if(String.class.equals(f.getType()) ){
                if(StrUtil.isNotBlank((String)val) && StrUtil.isNotBlank(columnName)){
                    ew.like(columnName,val);
                }
            }else if(dateClassList.contains(f.getType())
                    && propertyName.endsWith("Start")
                    && f.getAnnotation(TableField.class).exist()==false ){
                if(!Objects.isNull(val) && StrUtil.isNotBlank(columnName)){
                    ew.apply(" date_format("+columnName+",'"+sqlDateFormat+"')>=date_format({0},'"+sqlDateFormat+"')",val);
                }
            }else if(dateClassList.contains(f.getType())
                    && propertyName.endsWith("End")
                    && f.getAnnotation(TableField.class).exist()==false ){
                if(!Objects.isNull(val) && StrUtil.isNotBlank(columnName)){
                    ew.apply(" date_format("+columnName+",'"+sqlDateFormat+"')<=date_format({0},'"+sqlDateFormat+"')",val);
                }
            }else if(dateClassList.contains(f.getType())
                    && f.getAnnotation(TableField.class).exist()==true ){
                if(!Objects.isNull(val) && StrUtil.isNotBlank(columnName)){
                    ew.apply(" date_format("+columnName+",'"+sqlDateFormat+"')=date_format({0},'"+sqlDateFormat+"')",val);
                }
            }else if(f.getAnnotation(TableField.class).exist()==true
                    && !Objects.isNull(val) && StrUtil.isNotBlank(columnName)){
                ew.eq(columnName,val);
            }
        }

        if(orderMap!=null && orderMap.size()>0){
            orderMap.keySet().stream().forEach(key->{
                String item = orderMap.get(key);
                if("desc".equalsIgnoreCase(item)){
                    ew.orderByDesc(key);
                }else{
                    ew.orderByAsc(key);
                }
            });
        }

        if(searchMap!=null && searchMap.size()>0){
            searchMap.keySet().stream().forEach(key->{
                Object item = searchMap.get(key);
                if(StrUtil.isBlankOrUndefined(""+item)){
                    ew.isNull(key);
                }else if(item instanceof Object[]){
                    Object[] arr = (Object[])item;
                    List list = Arrays.asList(arr);
                    ew.in(key,list);
                }
            });
        }

        return ew;
    }

    public Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            return null;
        }
    }



}
